package org.hackystat.ui.chart.jfc.chartresource;

import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.hackystat.ui.chart.jfc.chartresource.dataset.DataFactoryImpl;
import org.hackystat.ui.chart.jfc.chartresource.dataset.TelemetryXYSeries;
import org.hackystat.ui.chart.jfc.server.Server;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleInsets;
import org.w3c.dom.Document;

/**
 * Represents the ChartManager which is responsible for the charts rendering.
 *
 * @author Pavel Senin.
 *
 */
public class ChartManager {

  /** the server handler. */
  private Server server;

  static final String dependencyToken = "dependency";

  /**
   * Instantiates the new manager.
   *
   * @param server the server handler.
   */
  public ChartManager(Server server) {
    this.server = server;
  }

  /**
   * Reports if queried telemetry.
   *
   * @param chartType the chart type queried.
   * @return whether or not this telemetrychart is available.
   */
  public boolean hasTelemetryType(String chartType) {
    // TODO Auto-generated method stub
    return false;
  }

  /**
   * Reports the list of all available telemtrycharts.
   *
   * @return the available charts list.
   */
  public Document getAvailableCharts() {
    // TODO Auto-generated method stub
    return null;
  }

  /**
   * Renders the chart and reports the resulting file name.
   *
   * @param chartType the string which defines the chart.
   * @param spanEnd the time period end.
   * @param spanStart the time period start.
   * @return the chart filename.
   * @throws ChartResourceException If error in redering charts.
   */
  public String renderChart(String chartType, String spanStart, String spanEnd)
      throws ChartResourceException {

    try {
      server.getLogger().warning(
          "Debuggy: asked to render chart for \"" + chartType + "\" from " + spanStart + " to "
              + spanEnd);
      if ("junit".equalsIgnoreCase(chartType)) {
        String chartName;
        chartName = generateJUnitChart(spanStart, spanEnd);
        return chartName;
      }
      else if ("dependency".equalsIgnoreCase(chartType)) {
        String chartName = generateDependencyChart(spanStart, spanEnd);
        return chartName;
      }
    }
    catch (DatatypeConfigurationException e) {
      server.getLogger().warning(e.toString());
    }
    return null;
  }

  /**
   * Generates chart according to parameters given.
   *
   * @param spanEnd the time period end.
   * @param spanStart the time period start.
   * @return the path to the chart generated.
   * @throws ChartResourceException If error in generating charts
   * @throws DatatypeConfigurationException if there is an error in numbers conversion.
   */
  public String generateJUnitChart(String spanStart, String spanEnd) throws ChartResourceException,
      DatatypeConfigurationException {

    /** Hold our stored chart name, it will be returned to the GWT caller. */
    String chartName = "";

    DatatypeFactory factory = DatatypeFactory.newInstance();
    XMLGregorianCalendar startCalendar = factory.newXMLGregorianCalendar(spanStart);
    GregorianCalendar gStart = startCalendar.toGregorianCalendar();
    Date startDate = gStart.getTime();

    XMLGregorianCalendar endCalendar = factory.newXMLGregorianCalendar(spanEnd);
    GregorianCalendar gEnd = endCalendar.toGregorianCalendar();
    Date endDate = gEnd.getTime();

    chartName = "newChart" + System.currentTimeMillis() + ".png";

    File chartFile = new File(this.server.getStoragePath() + "/" + chartName);

    // XYChartView view = new XYChartView(spanStart + " to " + spanEnd + " (Days)",
    // "UnitTests invocations", true);
    // view.setTitle("JUnit Chart Demo");
    // view.setDescription("Description for X-Y chart");
    //

    final DataFactoryImpl dataFactory = new DataFactoryImpl();

    Integer difference = dataFactory.calculateDifference(startDate, endDate);

    /** Generating individual time series. */
    TimeSeries JUnitSuccess = dataFactory.getDataUnitTests("test", "JUnit", "success", startDate,
        endDate, "day");

    TimeSeries JUnitFailure = dataFactory.getDataUnitTests("test", "JUnit", "failure", startDate,
        endDate, "day");

    /** Building timeseries collection. */
    TimeSeriesCollection timeseriescollection = new TimeSeriesCollection();
    timeseriescollection.addSeries(JUnitSuccess);
    timeseriescollection.addSeries(JUnitFailure);
    // timeseriescollection.setDomainIsPointsInTime(true);

    /** Building XY Dtataset. */

    JFreeChart jfreechart = ChartFactory.createTimeSeriesChart("JUnit Chart Demo", spanStart
        + " to " + spanEnd + " (Days)", "Unit Tests invocations", timeseriescollection, true, true,
        false);

    jfreechart.setBackgroundPaint(Color.white);
    XYPlot xyplot = (XYPlot) jfreechart.getPlot();
    xyplot.setBackgroundPaint(Color.lightGray);
    xyplot.setDomainGridlinePaint(Color.white);
    xyplot.setRangeGridlinePaint(Color.white);
    xyplot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));
    xyplot.setDomainCrosshairVisible(true);
    xyplot.setRangeCrosshairVisible(true);

    NumberAxis rangeAxis = (NumberAxis) xyplot.getRangeAxis();
    rangeAxis.setAutoRange(false);

    xyplot.getDomainAxis().setVerticalTickLabels(true);

    // double doubleAngle = 3.14159 * 45.0 / 180.0;
    // .getD.getRange().setCategoryLabelPositions(
    // CategoryLabelPositions.createUpRotationLabelPositions(doubleAngle));
    // // Adjust label position.
    // CategoryAxis xAxis = this.plot.getDomainAxis();
    // CategoryLabelPositions positions = xAxis.getCategoryLabelPositions();
    // CategoryLabelPosition position = positions.getLabelPosition(RectangleEdge.BOTTOM);
    // if (position.getCategoryAnchor() == RectangleAnchor.TOP &&
    // position.getLabelAnchor() == TextBlockAnchor.TOP_RIGHT) {
    // CategoryLabelPositions newPositions = CategoryLabelPositions.replaceBottomPosition(
    // positions, new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER_RIGHT,
    // TextAnchor.CENTER_RIGHT, -Math.PI / 2.0,
    // CategoryLabelWidthType.RANGE, 0.30f));
    // xAxis.setCategoryLabelPositions(newPositions);
    // rangeAxis.setLabelAngle(Math.PI / 2.0);

    org.jfree.chart.renderer.xy.XYItemRenderer xyitemrenderer = xyplot.getRenderer();
    if (xyitemrenderer instanceof XYLineAndShapeRenderer) {
      XYLineAndShapeRenderer xyLineandshaperenderer = (XYLineAndShapeRenderer) xyitemrenderer;
      xyLineandshaperenderer.setLinesVisible(true);
      xyLineandshaperenderer.setShapesVisible(true);
    }


    DateAxis dateaxis = (DateAxis) xyplot.getDomainAxis();
    dateaxis.setDateFormatOverride(new SimpleDateFormat("d-MMM", new Locale("en", "US")));

    if (difference < 15) {
      dateaxis.setTickUnit(new DateTickUnit(DateTickUnit.DAY, 1));
    }
    else if ((difference > 15) && (difference < 100)) {
      dateaxis.setTickUnit(new DateTickUnit(DateTickUnit.DAY, 7));
    }
    else if (difference > 100) {
      dateaxis.setTickUnit(new DateTickUnit(DateTickUnit.MONTH, 1));
    }

    NumberAxis numberAxis = (NumberAxis) xyplot.getRangeAxis();
    numberAxis.setNumberFormatOverride(new DecimalFormat("###"));
    numberAxis.setTickUnit(new NumberTickUnit(1.0));

    try {
      ChartUtilities.saveChartAsPNG(chartFile, jfreechart, 500, 450, null);
    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    /*
     * Finally, return the chart name to the caller.
     */
    return chartName;
  }

  /**
   * Generates chart according to parameters given.
   *
   * @param spanEnd the time period end.
   * @param spanStart the time period start.
   * @return the path to the chart generated.
   * @throws ChartResourceException If error in generating charts
   * @throws DatatypeConfigurationException if there is an error in numbers conversion.
   */
  public String generateDependencyChart(String spanStart, String spanEnd)
      throws ChartResourceException, DatatypeConfigurationException {

    /** Hold our stored chart name, it will be returned to the GWT caller. */
    String chartName = "";

    DatatypeFactory factory = DatatypeFactory.newInstance();
    XMLGregorianCalendar startCalendar = factory.newXMLGregorianCalendar(spanStart);
    GregorianCalendar gStart = startCalendar.toGregorianCalendar();
    Date startDate = gStart.getTime();

    XMLGregorianCalendar endCalendar = factory.newXMLGregorianCalendar(spanEnd);
    GregorianCalendar gEnd = endCalendar.toGregorianCalendar();
    Date endDate = gEnd.getTime();

    boolean afferentCouplings = true;
    boolean efferentCouplings = true;
    boolean cycles = true;

    final DataFactoryImpl dataFactory = new DataFactoryImpl();
    final XYSeriesCollection dataset = new XYSeriesCollection();

    if (afferentCouplings) {
      TelemetryXYSeries set1 = new TelemetryXYSeries();
      set1.setData(dataFactory.getData("test", ChartManager.dependencyToken, "afferent", startDate,
          endDate, "day"));
      dataset.addSeries(dataFactory.convertTelemetrySeries(set1, "Afferent Couplings"));
    }

    if (efferentCouplings) {
      TelemetryXYSeries set1 = new TelemetryXYSeries();
      set1.setData(dataFactory.getData("test", ChartManager.dependencyToken, "efferent", startDate,
          endDate, "day"));
      dataset.addSeries(dataFactory.convertTelemetrySeries(set1, "Efferent Couplings"));
    }

    if (cycles) {
      TelemetryXYSeries set1 = new TelemetryXYSeries();
      set1.setData(dataFactory.getData("test", ChartManager.dependencyToken, "cycles", startDate,
          endDate, "day"));
      dataset.addSeries(dataFactory.convertTelemetrySeries(set1, "Cycles"));
    }

    try {
      JFreeChart chart = this.createDependencyChart(dataset);

      chartName = "newChart" + System.currentTimeMillis() + ".png";

      File chartFile = new File(this.server.getStoragePath() + "/" + chartName);

      ChartUtilities.saveChartAsPNG(chartFile, chart, 500, 450, null);

    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    /*
     * Finally, return the chart name to the caller.
     */
    return chartName;
  }

  // /**
  // * Creates mock dataset and creates a chart.
  // *
  // * @return pie mock chart.
  // */
  // private JFreeChart generatePieChart() {
  // DefaultPieDataset dataset = new DefaultPieDataset();
  // Double cssValue = Math.random() * 100;
  // Double javaValue = Math.random() * 100;
  // Double htmlValue = Math.random() * 100;
  // dataset.setValue("CSS", cssValue);
  // dataset.setValue("Java", javaValue);
  // dataset.setValue("HTML", htmlValue);
  // final JFreeChart chart = ChartFactory.createPieChart("Languages Chart", dataset, false, false,
  // false);
  // chart.setBackgroundPaint(new Color(232, 238, 247));
  //    return chart;
  //  }

  /**
   * Creates a chart.
   *
   * @param dataset the data for the chart.
   *
   * @return a chart.
   */
  private JFreeChart createDependencyChart(final XYDataset dataset) {

    // create the chart...

    // ValueAxis xAxis = new DateAxis("time span");
    // ((DateAxis) xAxis).setVerticalTickLabels(true);

    final JFreeChart chart = ChartFactory.createXYLineChart("Mockup01 dependencies", // chart title
        "time span", // x axis label
        "dependency", // y axis label
        dataset, // data
        PlotOrientation.VERTICAL, true, // include legend
        true, // tooltips
        false // urls
        );

    // NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
    chart.setBackgroundPaint(new Color(232, 238, 247));

    // final StandardLegend legend = (StandardLegend) chart.getLegend();
    // legend.setDisplaySeriesShapes(true);

    // get a reference to the plot for further customisation...
    final XYPlot plot = chart.getXYPlot();
    plot.setBackgroundPaint(Color.lightGray);
    // plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 5.0, 5.0, 5.0, 5.0));
    plot.setDomainGridlinePaint(Color.white);
    plot.setRangeGridlinePaint(Color.white);

    final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
    // renderer.setSeriesLinesVisible(0, false);
    // renderer.setSeriesShapesVisible(1, false);
    plot.setRenderer(renderer);

    // change the auto tick unit selection to integer units only...
    final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
    rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());

    // OPTIONAL CUSTOMISATION COMPLETED.

    return chart;

  }
}
